package gamagora.game.world
{
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2World;
	
	import gamagora.game.entity.OSEntity;
	import gamagora.game.entity.player.OSPlayer;
	import gamagora.game.resources.OSImages;
	import gamagora.game.resources.OSSounds;
	import gamagora.game.states.OSPlayState;
	import gamagora.game.world.backgrounds.OSBackgroundManager;
	import gamagora.game.world.maps.OSMap;
	import gamagora.game.world.maps.OSMapFactory;
	
	import org.flixel.FlxCamera;
	import org.flixel.FlxG;
	import org.flixel.FlxGroup;
	import org.flixel.FlxSprite;
	import org.flixel.FlxText;

	public class OSWorld extends FlxGroup
	{
		private static const DEBUG_MAPS:Boolean = true;
		private static const PLAYER_START_X:int = 100; //100
		private static const PLAYER_START_Y:int = 830; //830
		private static const MID_MAP_WIDTH:uint = OSMap.WIDTH / 2;
		
		//Physique
		private var _physicWorld:b2World; 
		private var _physicDelta:Number;
		private var _physicVelocity:Number;
		private var _physicPosition:Number;
		
		//Cartes
		private var _currentMap:uint;
		private var _maps:Vector.<OSMap>;
		private var _nbMaps:uint;
		private var _nextMap:Boolean;
		private var _previousMap:Boolean;
		private var _visibleNext:Boolean;
		private var _visiblePrevious:Boolean;
		
		//Backgrounds
		private var _backgrounds:OSBackgroundManager;
		
		//Autres
		private var _player:OSPlayer;
		private var _sunBackground:FlxSprite;
		private var _hidePlayer:Boolean;
		
		//Debugs
		private var _nbPhyObj:FlxText;
		private var _nbFlxObj:FlxText;
		private var _mouseX:FlxText;
		private var _mouseY:FlxText;
		private var _playerX:FlxText;
		private var _mousePositionRel:FlxText;
		private var _background1PosRel:FlxText;
		private var _background2PosRel:FlxText;
		private var _background3PosRel:FlxText;
		
		public function OSWorld()
		{
			super();
			
			//Physique
			_physicWorld = new b2World(new b2Vec2(0, 9.8), true);
			_physicDelta = 1 / FlxG.framerate;
			_physicVelocity = 10;
			_physicPosition = 10;
			_physicWorld.SetContactListener(new OSContactListener());
			
			//Cartes
			createMaps();
			
			//Backgrounds
			_backgrounds = new OSBackgroundManager(_maps.length);
			addEntity(_backgrounds);
			
			//Player
			_player = new OSPlayer(PLAYER_START_X, PLAYER_START_Y);
			addEntity(_player);
			FlxG.camera.follow(_player, FlxCamera.STYLE_LOCKON);
			FlxG.camera.setBounds(0, 0, OSMap.WIDTH * _nbMaps, OSMap.HEIGHT);
			
			//Sun
			_sunBackground = new FlxSprite(0, 0, OSImages.BG);
			_sunBackground.scrollFactor.x = 0;
			add(_sunBackground);
			
			//Debugs
			if(DEBUG_MAPS)
			{
				_nbPhyObj = OSPlayState.addDebugText();
				_nbFlxObj = OSPlayState.addDebugText();
				_mouseX = OSPlayState.addDebugText();
				_mouseY = OSPlayState.addDebugText();
				_playerX = OSPlayState.addDebugText();
				_mousePositionRel = OSPlayState.addDebugText();
				_background1PosRel = OSPlayState.addDebugText();
				_background2PosRel = OSPlayState.addDebugText();
				_background3PosRel = OSPlayState.addDebugText();
			}
			
			OSSounds.playMusic();
		}
		
		//Override 
		
		public override function update():void
		{
			//Mise à jour de Flixel
			super.update();
			
			//Mise à jour de la physique
			_physicWorld.Step(_physicDelta, _physicVelocity, _physicPosition);
			_physicWorld.ClearForces();
			
			//Mise à jour du chargement des cartes
			updateMaps();
			
			//Débugs
			if(DEBUG_MAPS)
			{
				_nbPhyObj.text = (_physicWorld.GetBodyCount() - 1) + " bodies";
				_nbFlxObj.text = length + " sprites";
				_mouseX.text = "mx = " + FlxG.mouse.x.toFixed(1);
				_mouseY.text = "my = " + FlxG.mouse.y.toFixed(1);
				_playerX.text = "px = " + getPlayerX().toFixed(1);
				_mousePositionRel.text = "[map " + _currentMap + "] x = " + (FlxG.mouse.x - _currentMap * OSMap.WIDTH).toFixed(1);
				_background1PosRel.text = "[back 1] x = " + _backgrounds.layer1.getLayerScroll(FlxG.mouse.x).toFixed(1);
				_background2PosRel.text = "[back 2] x = " + _backgrounds.layer2.getLayerScroll(FlxG.mouse.x).toFixed(1);
				_background3PosRel.text = "[back 3] x = " + _backgrounds.layer3.getLayerScroll(FlxG.mouse.x).toFixed(1);
			}
			
			if(_player.y < 200)
			{
				if(Math.random() > 0.996)
				{
					OSSounds.playWind(0.05);
				}
			}
		}
		
		public override function draw():void
		{
			_sunBackground.draw();
			
			_backgrounds.draw();
			
			if(_visiblePrevious)
			{
				_maps[_currentMap - 1].drawBackgrounds();
			}
			else if(_visibleNext)
			{
				_maps[_currentMap + 1].drawBackgrounds();	
			}
			
			_maps[_currentMap].drawBackgrounds();
			
			//Objects
			if(_visiblePrevious)
			{
				_maps[_currentMap - 1].draw();
			}
			else if(_visibleNext)
			{
				_maps[_currentMap + 1].draw();	
			}
			
			_maps[_currentMap].draw();
			
			//Player
			if(!_hidePlayer)
			{
				_player.draw();
			}
			
			//Foregrounds
			if(_visiblePrevious)
			{
				_maps[_currentMap - 1].drawForegrounds();
			}
			else if(_visibleNext)
			{
				_maps[_currentMap + 1].drawForegrounds();	
			}
			
			_maps[_currentMap].drawForegrounds();
			
			
			//Mise à jour de la carte courante
			if(!_maps[_currentMap].inside(getPlayerX()))
			{ 
				if(_visibleNext && _currentMap + 1 < _maps.length)
				{
					_currentMap++;
				}
				else 
				{
					if(_visiblePrevious && _currentMap - 1 >= 0)
					{
						_currentMap--;	
					}
				}
			}
		}
		
		//Methods
		
		public function addEntity(entity:OSEntity):void
		{
			add(entity);
			entity.world = this;
			entity.addToWorld();
		}
		
		public function removeEntity(entity:OSEntity):void
		{
			remove(entity, true);
			entity.removeFromWorld();
		}
		
		public function getPlayerX():Number
		{
			return _player.x + _player.halfWidth;
		}
		
		public function getPlayerY():Number
		{
			return _player.y + _player.height; //Y au niveau du sol
		}
		
		public function getPlayerHalfWidth():Number
		{
			return _player.halfWidth;
		}
		
		public function hidePlayer():void
		{
			_hidePlayer = true;
		}
		
		public function stopBackgroundScroll():void
		{
			_backgrounds.stopScroll();
		}
		
		public function specialBackground():Boolean
		{
			return _maps[_currentMap].hasSpecialBackground();
		}
		
		private function createMaps():void
		{
			_currentMap = 0;
			_maps = new Vector.<OSMap>();
			OSMapFactory.GenerateMaps(_maps);
			_nbMaps = _maps.length;
			for(var i:uint=0;i<_nbMaps;i++)
			{
				_maps[i].setStartX(i * OSMap.WIDTH);
				_maps[i].world = this;
			}
			
			//Put the first map
			addEntity(_maps[_currentMap]);
		}
		
		private function updateMaps():void
		{
			_nextMap = _currentMap + 1 < _maps.length;
			_previousMap = _currentMap - 1 >= 0;
			_visibleNext = _visiblePrevious = false;
			
			if(_nextMap)
			{
				_visibleNext = _maps[_currentMap + 1].leftClosed(getPlayerX() + MID_MAP_WIDTH, 0);
				
				//Chargement des objets de la carte suivante à l'avance
				if(!_maps[_currentMap + 1].inWorld)
				{
					if(_maps[_currentMap + 1].leftClosed(getPlayerX() + MID_MAP_WIDTH))
					{
						addEntity(_maps[_currentMap + 1]);
						
						//Déchargement des objets de la carte précédente
						if(_previousMap && _maps[_currentMap - 1].inWorld)
						{
							removeEntity(_maps[_currentMap - 1]);
						}
					}
				}
			}
			
			if(_previousMap)
			{
				_visiblePrevious = _maps[_currentMap - 1].rightClosed(getPlayerX() - MID_MAP_WIDTH, 0);
				
				//Chargement des objets de la carte précédente à l'avance
				if(!_maps[_currentMap - 1].inWorld)
				{
					if(_maps[_currentMap - 1].rightClosed(getPlayerX() - MID_MAP_WIDTH))
					{
						addEntity(_maps[_currentMap - 1]);
						
						//Déchargement des objets de la carte suivante
						if(_nextMap && _maps[_currentMap + 1].inWorld)
						{
							removeEntity(_maps[_currentMap + 1]);
						}
					}
				}
			}
		}
		
		//Getters Setters

		public function get physicWorld():b2World
		{
			return _physicWorld;
		}

		public function get currentMap():uint
		{
			return _currentMap;
		}

		
	}
}
